/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.awt;
import gnu.java.awt.ClasspathToolkit;
import gnu.java.awt.EmbeddedWindow;
import gnu.java.awt.peer.ClasspathFontPeer;
import gnu.java.awt.peer.EmbeddedWindowPeer;
import gnu.java.security.action.GetPropertyAction;
import java.awt.AWTError;
import java.awt.AWTEvent;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.PrintJob;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Insets;
import java.awt.Point;
import java.awt.datatransfer.Clipboard;
import java.awt.event.ComponentEvent;
import java.awt.geom.AffineTransform;
import java.awt.im.InputMethodHighlight;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ImageConsumer;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.awt.image.VolatileImage;
import java.awt.peer.DesktopPeer;
import java.awt.peer.FontPeer;
import java.awt.peer.RobotPeer;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.imageio.ImageIO;
import javax.naming.NamingException;
import javax.swing.JDesktopPane;
import org.apache.log4j.Logger;
import org.jnode.awt.font.FontManager;
import org.jnode.awt.image.BufferedImageSurface;
import org.jnode.awt.image.JNodeImage;
import org.jnode.driver.DeviceException;
import org.jnode.driver.sound.speaker.SpeakerUtils;
import org.jnode.driver.video.AlreadyOpenException;
import org.jnode.driver.video.FrameBufferAPI;
import org.jnode.driver.video.FrameBufferAPIOwner;
import org.jnode.driver.video.Surface;
import org.jnode.driver.video.UnknownConfigurationException;
import org.jnode.naming.InitialNaming;
import org.jnode.annotation.SharedStatics;
import sun.awt.AppContext;
import sun.awt.SunToolkit;
import sun.awt.image.ToolkitImage;
/**
* @author epr
* @author Levente S\u00e1ntha
*/
@SharedStatics
public abstract class JNodeToolkit extends ClasspathToolkit implements FrameBufferAPIOwner {
protected static final Logger log = Logger.getLogger(JNodeToolkit.class);
private final Object initCloseLock = new Object();
private EventQueue waitingNativeQueue;
private Clipboard systemClipboard;
private FrameBufferAPI api;
private JNodeFrameBufferDevice fbDevice;
private JNodeGraphicsConfiguration config;
private JNodeEventQueue _eventQueue;
private LRUCache<Map<?, ?>, ClasspathFontPeer> fontCache = new LRUCache<Map<?, ?>, ClasspathFontPeer>(50);
private Surface graphics;
private boolean graphicsMode;
private KeyboardHandler keyboardHandler;
private MouseHandler mouseHandler;
private int refCount = 0;
private final Dimension screenSize = new Dimension(640, 480);
private Frame top;
private Runnable exitAction;
@SuppressWarnings("unused")
private Insets screenInsets;
public JNodeToolkit() {
refCount = 0;
systemClipboard = new Clipboard("JNodeSystemClipboard");
//initialize the main AppContext
AppContext appContext = AppContext.getAppContext();
synchronized (this) {
if (appContext.get(AppContext.EVENT_QUEUE_KEY) == null || _eventQueue == null) {
String eqName = System.getProperty("AWT.EventQueueClass", "org.jnode.awt.JNodeEventQueue");
try {
_eventQueue = (JNodeEventQueue) Class.forName(eqName).newInstance();
} catch (Exception e) {
e.printStackTrace();
System.err.println("Failed loading " + eqName + ": " + e);
_eventQueue = new JNodeEventQueue();
}
appContext.put(AppContext.EVENT_QUEUE_KEY, _eventQueue);
}
}
}
/**
* @see gnu.java.awt.ClasspathToolkit#createEmbeddedWindow(gnu.java.awt.EmbeddedWindow)
*/
@Override
public EmbeddedWindowPeer createEmbeddedWindow(EmbeddedWindow w) {
// TODO Auto-generated method stub
return null;
}
/**
* Gets the default toolkit casted to JNodeToolkit.
*
* @return The current toolkit casted to JNodeToolkit.
* @throws AWTError If the default toolkit is not instanceof JNodeToolkit.
*/
public static JNodeToolkit getJNodeToolkit() {
final Toolkit tk = Toolkit.getDefaultToolkit();
if (tk instanceof JNodeToolkit) {
return (JNodeToolkit) tk;
} else {
throw new AWTError("Toolkit is not a JNodeToolkit");
}
}
public static boolean isGuiActive() {
final Toolkit tk = Toolkit.getDefaultToolkit();
if (!(tk instanceof JNodeToolkit)) {
throw new AWTError("Toolkit is not a JNodeToolkit");
}
return (((JNodeToolkit) tk).graphics != null);
}
public static void startGui() {
final Toolkit tk = Toolkit.getDefaultToolkit();
if (!(tk instanceof JNodeToolkit)) {
throw new AWTError("Toolkit is not a JNodeToolkit");
}
((JNodeToolkit) tk).incRefCount();
}
public static void initGui() {
final Toolkit tk = Toolkit.getDefaultToolkit();
if (!(tk instanceof JNodeToolkit)) {
throw new AWTError("Toolkit is not a JNodeToolkit");
}
((JNodeToolkit) tk).incRefCount();
}
public static void stopGui() {
final Toolkit tk = Toolkit.getDefaultToolkit();
if (!(tk instanceof JNodeToolkit)) {
throw new AWTError("Toolkit is not a JNodeToolkit");
}
((JNodeToolkit) tk).decRefCount(true);
Toolkit.clearDefaultToolkit();
}
public static void refreshGui() {
final Toolkit tk = Toolkit.getDefaultToolkit();
if (!(tk instanceof JNodeToolkit)) {
throw new AWTError("Toolkit is not a JNodeToolkit");
}
((JNodeToolkit) tk).refresh();
}
protected void refresh() {
// Override me
}
public static void waitUntilStopped() {
final Toolkit tk = Toolkit.getDefaultToolkit();
if (!(tk instanceof JNodeToolkit)) {
throw new AWTError("Toolkit is not a JNodeToolkit");
}
((JNodeToolkit) tk).doWaitUntilStopped();
}
/**
* This method need only accessed from JNodeRobotPeer in the same package
*
* @return Returns the keyboardHandler.
*/
final KeyboardHandler getKeyboardHandler() {
return keyboardHandler;
}
/**
* This method need only accessed from JNodeRobotPeer in the same package
*
* @return Returns the mouseHandler.
*/
final MouseHandler getMouseHandler() {
return mouseHandler;
}
/**
* @see java.awt.Toolkit#beep()
*/
public void beep() {
SpeakerUtils.beep();
}
/**
* @see java.awt.Toolkit#checkImage(java.awt.Image, int, int, java.awt.image.ImageObserver)
*/
public int checkImage(Image image, int width, int height,
ImageObserver observer) {
int status = ImageObserver.ALLBITS | ImageObserver.WIDTH | ImageObserver.HEIGHT;
if (image instanceof JNodeImage) {
status = ((JNodeImage) image).checkImage();
}
if (observer != null)
observer.imageUpdate(image, status, -1, -1, image
.getWidth(observer), image.getHeight(observer));
return status;
}
/**
* JNode specific method. Create a buffered image compatible with the
* graphics configuration.
*
* @param width image width
* @param height image height
* @return The compatible image
*/
public BufferedImage createCompatibleImage(int width, int height) {
return config.createCompatibleImage(width, height);
}
/**
* @see gnu.java.awt.ClasspathToolkit#createRobot(java.awt.GraphicsDevice)
*/
public RobotPeer createRobot(GraphicsDevice screen) throws AWTException {
return new JNodeRobotPeer<JNodeToolkit>(this, screen);
}
/**
* @see gnu.java.awt.ClasspathToolkit#createFont(int, java.io.InputStream)
*/
public Font createFont(int format, InputStream stream) throws FontFormatException, IOException {
return getFontManager().createFont(format, stream);
}
/**
* @see java.awt.Toolkit#createImage(byte[], int, int)
*/
public Image createImage(byte[] data, int offset, int len) {
try {
return ImageIO.read(new ByteArrayInputStream(data, offset, len));
} catch (IOException e) {
log.error("Image read error", e);
// let it fall through to default code
}
return new ErrorImage();
}
/**
* @see java.awt.Toolkit#createImage(java.awt.image.ImageProducer)
*/
public Image createImage(ImageProducer producer) {
ToolkitImage image = new ToolkitImage(producer);
//force preloading
image.getWidth();
return image;
}
/**
* @see java.awt.Toolkit#createImage(java.lang.String)
*/
public Image createImage(String filename) {
return getImage(filename);
}
/**
* @see java.awt.Toolkit#createImage(java.net.URL)
*/
public Image createImage(URL url) {
return getImage(url);
}
public VolatileImage createVolatileImage(int width, int height) {
//TODO implement volatile image support
return null;
//throw new RuntimeException("Not implemented");
}
/**
* Decrement the peer reference count.
*
* @param forceClose if true the gui is always closed
* @return the reference count
*/
private int decRefCount(boolean forceClose) {
final int rc;
synchronized (initCloseLock) {
refCount--;
rc = refCount;
}
log.debug("refCount.dec=" + rc);
if ((rc == 0) || forceClose) {
onClose();
final KeyboardHandler keyboardHandler = this.keyboardHandler;
final MouseHandler mouseHandler = this.mouseHandler;
final Surface graphics = this.graphics;
if (keyboardHandler != null) {
keyboardHandler.close();
}
if (mouseHandler != null) {
mouseHandler.close();
}
if (graphics != null) {
graphics.close();
}
final FrameBufferAPI savedApi = this.api;
this.api = null;
this.graphics = null;
this.keyboardHandler = null;
this.mouseHandler = null;
// Shutdown the eventqueue as the last event
final JNodeEventQueue eventQueue = this._eventQueue;
if (eventQueue != null) {
EventQueue.invokeLater(new Runnable() {
public void run() {
eventQueue.shutdown();
}
});
}
synchronized (initCloseLock) {
this.refCount = 0;
graphicsMode = false;
initCloseLock.notifyAll();
}
savedApi.releaseOwnership(this);
return 0;
} else {
return rc;
}
}
private void doWaitUntilStopped() {
synchronized (initCloseLock) {
while (graphicsMode) {
try {
initCloseLock.wait();
} catch (InterruptedException ex) {
// Ignore
}
}
}
}
/**
* Gets the AWT context.
*
* @return the AWT context
*/
public abstract JNodeAwtContext getAwtContext();
/**
* Newer method to produce a peer for a Font object, even though Sun's
* design claims Font should now be peerless, we do not agree with this
* model, hence "ClasspathFontPeer".
*/
@SuppressWarnings("unchecked")
public ClasspathFontPeer getClasspathFontPeer(String name, Map attrs) {
final Map<String, String> keyMap = new HashMap<String, String>(attrs);
// We don't know what kind of "name" the user requested (logical, face,
// family), and we don't actually *need* to know here. The worst case
// involves failure to consolidate fonts with the same backend in our
// cache. This is harmless.
keyMap.put("JNodeToolkit.RequestedFontName", name);
if (fontCache.containsKey(keyMap))
return fontCache.get(keyMap);
else {
ClasspathFontPeer newPeer = getFontManager().createFontPeer(name, attrs);
fontCache.put(keyMap, newPeer);
return newPeer;
}
}
/**
* @return The model
* @see java.awt.Toolkit#getColorModel()
*/
public ColorModel getColorModel() {
return ColorModel.getRGBdefault();
}
/**
* @return The fonts
* @see java.awt.Toolkit#getFontList()
*/
public String[] getFontList() {
Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment()
.getAllFonts();
String[] names = new String[fonts.length];
for (int i = 0; i < fonts.length; i++) {
names[i] = fonts[i].getName();
}
return names;
}
/**
* Gets the font manager, or null if not found.
*
* @return The font mananger
*/
public FontManager getFontManager() {
try {
return InitialNaming.lookup(FontManager.NAME);
} catch (NamingException ex) {
return null;
}
}
/**
* @see java.awt.Toolkit#getFontMetrics(java.awt.Font)
*/
public FontMetrics getFontMetrics(Font font) {
final FontManager fm = getFontManager();
if (fm != null) {
return fm.getFontMetrics(font);
} else {
return null;
}
}
/**
* @see java.awt.Toolkit#getFontPeer(String, int)
*/
protected final FontPeer getFontPeer(String name, int style) {
// All fonts get a default size of 12 if size is not specified.
return getFontPeer(name, style, 12);
}
/**
* Private method that allows size to be set at initialization time.
*
* @param name the font name
* @param style the font style
* @param size the font size
* @return the font peer
*/
@SuppressWarnings("unchecked")
private FontPeer getFontPeer(String name, int style, int size) {
Map attrs = new HashMap();
ClasspathFontPeer.copyStyleToAttrs(style, attrs);
ClasspathFontPeer.copySizeToAttrs(size, attrs);
return getClasspathFontPeer(name, attrs);
}
/**
* @return The surface
*/
public final Surface getGraphics() {
return this.graphics;
}
/**
* @return The configuration
* @see java.awt.peer.ComponentPeer#getGraphicsConfiguration()
*/
public final GraphicsConfiguration getGraphicsConfiguration() {
return config;
}
/**
* Test if the image is valid (!= null), otherwise return an error image.
*
* @param img the image to test
* @return the image if img is not null, an error image otherwise
*/
private Image testErrorImage(Image img) {
if (img == null) {
return new ErrorImage();
} else {
return img;
}
}
/**
* @see java.awt.Toolkit#getImage(java.lang.String)
*/
public Image getImage(final String filename) {
log.debug("getImage(" + filename + ")");
return testErrorImage(AccessController.doPrivileged(new PrivilegedAction<Image>() {
public Image run() {
try {
final String userDir = AccessController.doPrivileged(new GetPropertyAction("user.dir"));
Image image = getImage(new URL("file:" + new File(userDir, filename)));
return image != null ? image : getImage(new URL("file:" + new File(filename).getAbsolutePath()));
} catch (Exception ex) {
log.debug("Error loading image", ex);
}
return null;
}
}));
}
/**
* @see java.awt.Toolkit#getImage(java.net.URL)
*/
public Image getImage(final URL url) {
return testErrorImage(AccessController.doPrivileged(new PrivilegedAction<Image>() {
public Image run() {
try {
return ImageIO.read(url);
} catch (Exception ex) {
log.debug("Exception during getImage", ex);
}
return null;
}
}));
}
/**
* @see gnu.java.awt.ClasspathToolkit#getLocalGraphicsEnvironment()
*/
public GraphicsEnvironment getLocalGraphicsEnvironment() {
return new JNodeGraphicsEnvironment();
}
/**
* @see java.awt.Toolkit#getPrintJob(java.awt.Frame, java.lang.String,
* java.util.Properties)
*/
public PrintJob getPrintJob(Frame frame, String title, Properties props) {
// TODO Auto-generated method stub
return null;
}
/**
* @return int
* @see java.awt.Toolkit#getScreenResolution()
*/
public int getScreenResolution() {
// TODO Auto-generated method stub
return 0;
}
/**
* @return The screen size
* @see java.awt.Toolkit#getScreenSize()
*/
public Dimension getScreenSize() {
return new Dimension(screenSize);
}
/**
* @return The clipboard
* @see java.awt.Toolkit#getSystemClipboard()
*/
public Clipboard getSystemClipboard() {
return systemClipboard;
}
/**
* @return The event queue
*/
protected final EventQueue getSystemEventQueueImpl() {
AppContext ac = AppContext.getAppContext();
if (ac != null) {
EventQueue eq = (EventQueue) ac.get(AppContext.EVENT_QUEUE_KEY);
if (eq != null) {
return eq;
}
}
if ((_eventQueue == null) || (!_eventQueue.isLive() && isGuiActive())) {
synchronized (this) {
if ((_eventQueue == null) || (!_eventQueue.isLive() && isGuiActive())) {
_eventQueue = new JNodeEventQueue();
}
if (ac != null && ac.get(AppContext.EVENT_QUEUE_KEY) == null) {
ac.put(AppContext.EVENT_QUEUE_KEY, _eventQueue);
}
}
}
return _eventQueue;
}
public final synchronized EventQueue getMainEventQueue() {
return _eventQueue;
}
public Frame getTop() {
return top;
}
/**
* Gets the top most visible component at a given location.
*
* @param x the x coordiante
* @param y the y coordinate
* @return the component
*/
public Component getTopComponentAt(int x, int y) {
final Frame f = getTop();
if (f == null) {
return null;
}
Component c = f.findComponentAt(x, y);
if (c == null) {
c = f;
}
return c;
}
/**
* Increment the peer reference count
*
* @return the reference count
*/
private int incRefCount() {
final boolean initialize;
final int rc;
synchronized (initCloseLock) {
refCount++;
rc = refCount;
initialize = (refCount == 1);
}
log.debug("refCount.inc=" + rc);
if (initialize) {
fbDevice = (JNodeFrameBufferDevice) GraphicsEnvironment
.getLocalGraphicsEnvironment().getDefaultScreenDevice();
if (fbDevice == null) {
throw new AWTError("No framebuffer fbDevice found");
}
log.info("Supported graphics configurations: ");
GraphicsConfiguration[] configurations = fbDevice.getConfigurations();
for (GraphicsConfiguration g_conf : configurations) {
log.info(g_conf);
}
String screen_size = AccessController.doPrivileged(new GetPropertyAction("jnode.awt.screensize", "none"));
if ("none".equals(screen_size)) {
config = (JNodeGraphicsConfiguration) fbDevice.getDefaultConfiguration();
} else {
boolean found = false;
for (GraphicsConfiguration g_conf : configurations) {
if (screen_size.equals(g_conf.toString())) {
config = (JNodeGraphicsConfiguration) g_conf;
found = true;
break;
}
}
if (!found) {
config = (JNodeGraphicsConfiguration) fbDevice.getDefaultConfiguration();
}
}
log.info("Using: " + config);
this.api = fbDevice.getAPI();
try {
log.debug("Opening AWT: Using fbDevice " + fbDevice.getIDstring());
api.requestOwnership(this);
this.graphics = api.open(config.getConfig());
if (graphics == null) {
log.debug("No Graphics for fbDevice: " + fbDevice.getIDstring());
return rc;
}
graphicsMode = true;
screenSize.width = config.getConfig().getScreenWidth();
screenSize.height = config.getConfig().getScreenHeight();
//drawStartupScreen();
final EventQueue eventQueue = getSystemEventQueueImpl();
this.keyboardHandler = new KeyboardHandler(eventQueue);
this.mouseHandler = new MouseHandler(fbDevice.getDevice(),
screenSize, eventQueue, keyboardHandler);
keyboardHandler.install();
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
onInitialize();
return null;
}
});
this.refCount = rc;
} catch (DeviceException ex) {
decRefCount(true);
throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
} catch (UnknownConfigurationException ex) {
decRefCount(true);
throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
} catch (AlreadyOpenException ex) {
decRefCount(true);
throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
} catch (Throwable ex) {
decRefCount(true);
log.error("Unknown exception", ex);
throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
}
}
return rc;
}
public void updateCursor(Cursor cur) {
if (mouseHandler != null) {
int type = cur.getType();
switch (type) {
case Cursor.DEFAULT_CURSOR:
mouseHandler.setCursorImage(JNodeCursors.ARROW);
break;
case Cursor.TEXT_CURSOR:
mouseHandler.setCursorImage(JNodeCursors.TEXT);
break;
case Cursor.E_RESIZE_CURSOR:
case Cursor.W_RESIZE_CURSOR:
mouseHandler.setCursorImage(JNodeCursors.RESIZE_HORIZONTAL);
break;
case Cursor.N_RESIZE_CURSOR:
case Cursor.S_RESIZE_CURSOR:
mouseHandler.setCursorImage(JNodeCursors.RESIZE_VERTICAL);
break;
case Cursor.NE_RESIZE_CURSOR:
case Cursor.SW_RESIZE_CURSOR:
mouseHandler.setCursorImage(JNodeCursors.RESIZE_NORTHEAST);
break;
case Cursor.NW_RESIZE_CURSOR:
case Cursor.SE_RESIZE_CURSOR:
mouseHandler.setCursorImage(JNodeCursors.RESIZE_NORTHWEST);
break;
case Cursor.HAND_CURSOR:
mouseHandler.setCursorImage(JNodeCursors.HAND);
break;
case Cursor.WAIT_CURSOR:
mouseHandler.setCursorImage(JNodeCursors.WAIT);
break;
case Cursor.MOVE_CURSOR:
mouseHandler.setCursorImage(JNodeCursors.MOVE);
break;
case Cursor.CROSSHAIR_CURSOR:
mouseHandler.setCursorImage(JNodeCursors.CROSSHAIR);
break;
default:
mouseHandler.setCursorImage(JNodeCursors.ARROW);
}
}
}
@SuppressWarnings("unused")
private void drawStartupScreen() {
AffineTransform tx = new AffineTransform();
graphics.fill(new Rectangle(0, 0, config.getBounds().width, config.getBounds().height), null, tx, Color.BLACK,
Surface.PAINT_MODE);
for (int i = 0; i < 100; i++)
graphics.draw(new Rectangle(100 + i, 100 + i, config.getBounds().width - 2 * (100 + i),
config.getBounds().height - 2 * (100 + i)), null, tx, (i % 2 == 0) ? Color.RED : Color.BLUE,
Surface.PAINT_MODE);
}
private JNodeFrameBufferDevice getDevice() {
final JNodeFrameBufferDevice device =
(JNodeFrameBufferDevice) GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
if (device == null) {
throw new AWTError("No framebuffer fbDevice found");
}
return device;
}
private GraphicsConfiguration[] configs;
public GraphicsConfiguration[] getConfigurations() {
if (configs == null) {
final GraphicsConfiguration[] configurations = getDevice().getConfigurations();
configs = new GraphicsConfiguration[configurations.length];
System.arraycopy(configurations, 0, configs, 0, configurations.length);
Arrays.sort(configs, new Comparator<GraphicsConfiguration>() {
@Override
public int compare(GraphicsConfiguration o1, GraphicsConfiguration o2) {
final Rectangle b1 = o1.getBounds();
final Rectangle b2 = o2.getBounds();
int comp;
if (b1.getWidth() > b2.getWidth()) {
comp = +1;
} else if (b1.getWidth() < b2.getWidth()) {
comp = -1;
} else {
if (b1.getHeight() > b2.getHeight()) {
comp = +1;
} else if (b1.getHeight() < b2.getHeight()) {
comp = -1;
} else {
comp = 0;
}
}
return comp;
}
});
}
return configs;
}
public Dimension changeScreenSize(JNodeGraphicsConfiguration config) {
final JNodeFrameBufferDevice device = getDevice();
this.config = config;
log.info("Using: " + config);
this.api = device.getAPI();
try {
float xs = (float) mouseHandler.getX() / (float) screenSize.width;
float ys = (float) mouseHandler.getY() / (float) screenSize.height;
//close the old stuff
this.graphics.close();
this.mouseHandler.close();
//open the new
this.graphics = api.open(config.getConfig());
if (graphics == null) {
log.debug("No Graphics for fbDevice: " + device.getIDstring());
}
screenSize.width = config.getConfig().getScreenWidth();
screenSize.height = config.getConfig().getScreenHeight();
this.mouseHandler =
new MouseHandler(device.getDevice(), screenSize, getSystemEventQueueImpl(), keyboardHandler);
mouseHandler.setCursor((int) (xs * screenSize.width), (int) (ys * screenSize.height));
getAwtContext().adjustDesktopSize(screenSize.width, screenSize.height);
onResize();
device.setDefaultConfiguration(this.config);
return getScreenSize();
} catch (Exception e) {
throw (AWTError) new AWTError(e.getMessage()).initCause(e);
}
}
BufferedImage backBuffer;
/**
* Leave the gui mode
*/
public final void leaveGUI() {
Dimension ss = getScreenSize();
backBuffer = new BufferedImage((int) ss.getWidth(), (int) ss.getHeight(), BufferedImage.TYPE_INT_ARGB);
final KeyboardHandler keyboardHandler = this.keyboardHandler;
final MouseHandler mouseHandler = this.mouseHandler;
final Surface graphics = this.graphics;
this.graphics = new BufferedImageSurface(backBuffer);
if (keyboardHandler != null) {
keyboardHandler.close();
}
if (mouseHandler != null) {
mouseHandler.close();
}
if (graphics != null) {
graphics.close();
}
this.keyboardHandler = null;
this.mouseHandler = null;
synchronized (initCloseLock) {
graphicsMode = false;
initCloseLock.notifyAll();
}
api.releaseOwnership(this);
}
/**
* Join the GUI mode
*/
public final void joinGUI() {
try {
api.requestOwnership(this);
this.graphics = api.open(config.getConfig());
this.keyboardHandler = new KeyboardHandler(_eventQueue);
this.mouseHandler = new MouseHandler(fbDevice.getDevice(),
screenSize, _eventQueue, keyboardHandler);
keyboardHandler.install();
getAwtContext().getAwtRoot().repaint();
synchronized (initCloseLock) {
graphicsMode = true;
}
} catch (DeviceException ex) {
decRefCount(true);
throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
} catch (UnknownConfigurationException ex) {
decRefCount(true);
throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
} catch (AlreadyOpenException ex) {
decRefCount(true);
throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
} catch (Throwable ex) {
decRefCount(true);
log.error("Unknown exception", ex);
throw (AWTError) new AWTError(ex.getMessage()).initCause(ex);
}
}
public void iterateNativeQueue(EventQueue locked, boolean block) {
if (block) {
this.waitingNativeQueue = locked;
synchronized (locked) {
try {
// Wait for as long as the human eye can tolerate it.
// We wait 100ms.
locked.wait(100);
} catch (InterruptedException e) {
// Ignore
}
this.waitingNativeQueue = null;
}
}
}
/**
* @see java.awt.Toolkit#mapInputMethodHighlight(java.awt.im.InputMethodHighlight)
*/
@SuppressWarnings("unchecked")
public Map mapInputMethodHighlight(InputMethodHighlight highlight) {
// TODO Auto-generated method stub
return null;
}
public boolean nativeQueueEmpty() {
return true;
}
protected abstract void onClose();
protected abstract void onInitialize();
protected abstract void onResize();
public abstract boolean isWindow(Component comp);
/**
* @see java.awt.Toolkit#prepareImage(java.awt.Image, int, int,
* java.awt.image.ImageObserver)
*/
public boolean prepareImage(Image image, int width, int height,
ImageObserver observer) {
if (image instanceof JNodeImage) {
final JNodeImage i = (JNodeImage) image;
return i.prepare(observer);
} else {
return true;
}
}
protected void setTop(Frame frame) {
this.top = frame;
}
/**
* @see java.awt.Toolkit#sync()
*/
public void sync() {
// TODO Auto-generated method stub
}
public void wakeNativeQueue() {
final EventQueue q = this.waitingNativeQueue;
if (q != null) {
synchronized (q) {
q.notifyAll();
}
}
}
public int getMouseNumberOfButtons() {
//todo implement it
return super.getMouseNumberOfButtons();
}
public void activateWindow(Component source) {
}
/**
* A helper class to return to clients in cases where a BufferedImage is
* desired but its construction fails.
*/
private class ErrorImage extends Image {
public ErrorImage() {
}
public int getWidth(ImageObserver observer) {
return 1;
}
public int getHeight(ImageObserver observer) {
return 1;
}
public ImageProducer getSource() {
return new ImageProducer() {
Set<ImageConsumer> consumers = new HashSet<ImageConsumer>();
public synchronized void addConsumer(ImageConsumer ic) {
consumers.add(ic);
}
public synchronized boolean isConsumer(ImageConsumer ic) {
return consumers.contains(ic);
}
public synchronized void removeConsumer(ImageConsumer ic) {
consumers.remove(ic);
}
public synchronized void startProduction(ImageConsumer ic) {
consumers.add(ic);
for (ImageConsumer c : consumers) {
c.imageComplete(ImageConsumer.IMAGEERROR);
}
}
public void requestTopDownLeftRightResend(ImageConsumer ic) {
startProduction(ic);
}
};
}
public Graphics getGraphics() {
return null;
}
public Object getProperty(String name, ImageObserver observer) {
return null;
}
public Image getScaledInstance(int width, int height, int flags) {
return new ErrorImage();
}
public void flush() {
}
}
@SuppressWarnings("serial")
private class LRUCache<K, V> extends java.util.LinkedHashMap<K, V> {
int max_entries;
public LRUCache(int max) {
super(max, 0.75f, true);
max_entries = max;
}
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > max_entries;
}
}
public boolean isModalExclusionTypeSupported(Dialog.ModalExclusionType modalExclusionType) {
//todo implementit
return false;
}
protected DesktopPeer createDesktopPeer(Desktop target) throws HeadlessException {
//todo implementit
return null;
}
@Override
public void ownershipLost() {
if (isGuiActive()) {
leaveGUI();
}
}
@Override
public void ownershipGained() {
startAwt();
}
static void startAwt() {
if (JNodeToolkit.isGuiActive()) {
((JNodeToolkit) Toolkit.getDefaultToolkit()).joinGUI();
JNodeToolkit.waitUntilStopped();
} else {
JNodeToolkit.startGui();
try {
final String desktopClassName = System.getProperty("jnode.desktop");
if (desktopClassName != null) {
final Class<?> desktopClass =
Thread.currentThread().getContextClassLoader().loadClass(desktopClassName);
final Object desktop = desktopClass.newInstance();
if (desktop instanceof Runnable) {
final Thread t = new Thread((Runnable) desktop);
t.start();
}
}
} catch (ClassNotFoundException ex) {
log.error("Cannot find desktop class", ex);
} catch (InstantiationException ex) {
log.error("Cannot instantiate desktop class", ex);
} catch (IllegalAccessException ex) {
log.error("Cannot access desktop class", ex);
} finally {
JNodeToolkit.waitUntilStopped();
}
}
((JNodeToolkit) Toolkit.getDefaultToolkit()).runExitAction();
}
/**
* Set the action to be performed after the GUI has been shutdown, and
* before control is returned to (for instance) the CommandShell.
*
* @param exitAction an action, or <code>null</code>.
*/
public static void setExitAction(Runnable exitAction) {
// FIXME ... This method probably needs a security check. (The way it
// is currently used potentially offers a small window for some other
// thread to insert an action that would then be executed in the security
// context of the GUI's owner.)
((JNodeToolkit) Toolkit.getDefaultToolkit()).exitAction = exitAction;
}
private synchronized void runExitAction() {
if (exitAction != null) {
exitAction.run();
}
}
/**
* Post the given event on the system eventqueue.
*/
public final void postEvent(AWTEvent event) {
Object source = event.getSource();
if (source instanceof Component) {
AppContext ac = SunToolkit.targetToAppContext(source);
if (ac != null) {
java.awt.EventQueue eq = (java.awt.EventQueue) ac.get(sun.awt.AppContext.EVENT_QUEUE_KEY);
if (eq != null) {
eq.postEvent(event);
return;
}
}
}
getSystemEventQueueImpl().postEvent(event);
}
public static void postToTarget(ComponentEvent event, Component target) {
EventQueue queue;
AppContext ac = SunToolkit.targetToAppContext(target);
if (ac == null) {
queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
} else {
queue = (EventQueue) ac.get(sun.awt.AppContext.EVENT_QUEUE_KEY);
if (queue == null) {
queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
}
}
queue.postEvent(event);
}
@Override
public Insets getScreenInsets(GraphicsConfiguration gc) throws HeadlessException {
JNodeAwtContext awtc = getAwtContext();
if (awtc == null)
return super.getScreenInsets(gc);
Component root = awtc.getTopLevelRootComponent();
if (root == null)
return super.getScreenInsets(gc);
JDesktopPane jdp = awtc.getDesktop();
if (jdp == null)
return super.getScreenInsets(gc);
Rectangle trc_bounds = root.getBounds();
Point jdp_loc = jdp.getLocationOnScreen();
Rectangle jdp_bounds = jdp.getBounds();
return new Insets(jdp_loc.y, jdp_loc.x, trc_bounds.height - jdp_loc.y - jdp_bounds.height,
trc_bounds.width - jdp_loc.x - jdp_bounds.width);
}
}